package com.harmontronics.hanma.paas.paas.domain.app.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Tuple;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.harmontronics.hanma.paas.paas.common.PageBase;
import com.harmontronics.hanma.paas.paas.common.ResponseEntity;
import com.harmontronics.hanma.paas.paas.consts.DbConstants;
import com.harmontronics.hanma.paas.paas.consts.EnvEnum;
import com.harmontronics.hanma.paas.paas.consts.HttpMethodEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.FieldRelationInfo;
import com.harmontronics.hanma.paas.paas.domain.app.entity.HmHttpRequestDefinition;
import com.harmontronics.hanma.paas.paas.domain.app.entity.HmModel;
import com.harmontronics.hanma.paas.paas.domain.app.entity.TreeNode;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.AuthTypeEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.BodyTypeEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.BusinessServiceTypeEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.EntityModelTypeEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.Fields;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.I18nKeyEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.ModelTypeEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.RelationTypeEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.ServiceTypeConstant;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmDictionaryRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmEntityModelRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmHttpRequestDefinitionRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmHttpServiceRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmModelRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HttpServiceSettingRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmDictionaryPO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmEntityModelPO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmHttpRequestDefinitionPO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmHttpServicePO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmModelPO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HttpServiceSettingPO;
import com.harmontronics.hanma.paas.paas.domain.app.service.dialect.DialectDecision;
import com.harmontronics.hanma.paas.paas.domain.app.service.dialect.PostgresDialectService;
import com.harmontronics.hanma.paas.paas.domain.app.service.tools.FieldDisplayRuleBO;
import com.harmontronics.hanma.paas.paas.domain.app.service.tools.FieldDisplayRuleParser;
import com.harmontronics.hanma.paas.paas.domain.app.service.tools.HttpServiceHelper;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.persistence.HmOrgRepository;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.persistence.HmUserRepository;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.po.HmOrgPO;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.po.HmUserPO;
import com.harmontronics.hanma.paas.paas.exception.ErrorKeyException;
import com.harmontronics.hanma.paas.paas.utils.ConvertUtil;
import com.harmontronics.hanma.paas.paas.utils.OkHttpUtils;
import com.harmontronics.hanma.paas.paas.utils.StringUtil;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.TypeRef;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.apache.commons.text.StringSubstitutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * @author ldb
 */
@Service("hmHttpRequestDefinitionService")
@DS("#header.App-Unique")
public class HmHttpRequestDefinitionService {

    /**
     * APP统一方法参数名: 关联类型
     */
    public final static String PARAM_NAME_RELATION_TYPE = "relationType";
    public final static String PARAM_NAME_DICTIONARY_TYPE = "value";
    public final static String PARAM_NAME_KEYWORD = "keyword";
    public final static String PARAM_NAME_LIMIT = "limit";
    public final static String PARAM_NAME_BUSINESS_RELATION_MODEL_CODE = "modelCode";
    public final static String PARAM_NAME_BUSINESS_DISPLAY_RULE = "value";
    public final static String ERROR_VALUE_MISSING = "值缺失";

    private static final Pattern PATTERN = Pattern.compile("^(\\$.[\\w.]+)\\s*==\\s*['\"]?([\\w.]+)['\"]?\\?\\s*([\\d]+)\\s*:\\s*(\\d+)$");
    @Autowired
    private HmHttpRequestDefinitionRepository hmHttpRequestDefinitionRepository;
    @Autowired
    private HmModelRepository hmModelRepository;
    @Autowired
    private HmModelService hmModelService;
    @Autowired
    private HmHttpServiceRepository hmHttpServiceRepository;
    @Autowired
    private HttpServiceSettingRepository httpServiceSettingRepository;
    @Value("${app.env:DEV}")
    private String env;
    @Autowired
    private OkHttpClient okHttpClient;
    @Autowired
    private DialectDecision dialectDecision;
    @Autowired
    private HmDictionaryRepository hmDictionaryRepository;
    @Autowired
    private HmEntityModelRepository hmEntityModelRepository;
    @Autowired
    private HmScriptService hmScriptService;
    @Autowired
    private PostgresDialectService postgresDialectService;

    /**
     * 注意：代码拆分
     */
    @Autowired
    private HmOrgRepository hmOrgRepository;

    @Autowired
    private HmUserRepository hmUserRepository;

    @Autowired
    private HttpServiceHelper httpServiceHelper;

    /**
     * 临时缓存
     */
    // TODO - tylr 2021/6/8 16:58 作为成员变量，无法试下临时缓存的清理
    private Map<Long, HmOrgPO> orgTempCache =Maps.newConcurrentMap();
    private Map<Long, HmUserPO> usersTemCache =Maps.newConcurrentMap();

    private List<HmOrgPO> findOrgByIds(List<Long> ids){
        if(CollectionUtil.isNotEmpty(ids)){
            if(orgTempCache.isEmpty()){
                orgTempCache = hmOrgRepository.findAllOrg();
            }
            return orgTempCache.values().stream().filter(item->ids.contains(item.getId())).collect(
                    Collectors.toList());
        }
        return null;
    }

    private List<HmUserPO> findUsersByIds(List<Long> ids){
        if(CollectionUtil.isNotEmpty(ids)){
            if(usersTemCache.isEmpty()){
                usersTemCache = hmUserRepository.findAllUsers();
            }
            return usersTemCache.values().stream().filter(item->ids.contains(item.getId())).collect(
                    Collectors.toList());
        }
        return null;
    }


    public void addDefaultForEntityModel(Long modelId, boolean isView,boolean supportTree) {
        if(!isView) {
            HmHttpRequestDefinitionPO insertPo = new HmHttpRequestDefinitionPO();
            insertPo.setModelId(modelId);
            insertPo.setKey(ServiceTypeConstant.INSERT);
            insertPo.setServiceType(BusinessServiceTypeEnum.BUILT_IN);
            insertPo.setName("新增");
            hmHttpRequestDefinitionRepository.save(insertPo);
            HmHttpRequestDefinitionPO updatePo = new HmHttpRequestDefinitionPO();
            updatePo.setModelId(modelId);
            updatePo.setKey(ServiceTypeConstant.UPDATE);
            updatePo.setServiceType(BusinessServiceTypeEnum.BUILT_IN);
            updatePo.setName("修改");
            hmHttpRequestDefinitionRepository.save(updatePo);
            HmHttpRequestDefinitionPO deletePo = new HmHttpRequestDefinitionPO();
            deletePo.setModelId(modelId);
            deletePo.setKey(ServiceTypeConstant.DELETE);
            deletePo.setServiceType(BusinessServiceTypeEnum.BUILT_IN);
            deletePo.setName("删除");
            hmHttpRequestDefinitionRepository.save(deletePo);
            HmHttpRequestDefinitionPO selectOnePo = new HmHttpRequestDefinitionPO();
            selectOnePo.setModelId(modelId);
            selectOnePo.setKey(ServiceTypeConstant.SELECT_ONE);
            selectOnePo.setServiceType(BusinessServiceTypeEnum.BUILT_IN);
            selectOnePo.setName("根据ID查询");
            hmHttpRequestDefinitionRepository.save(selectOnePo);
        }
        HmHttpRequestDefinitionPO selectListPo = new HmHttpRequestDefinitionPO();
        selectListPo.setModelId(modelId);
        selectListPo.setKey(ServiceTypeConstant.SELECT_LIST);
        selectListPo.setServiceType(BusinessServiceTypeEnum.BUILT_IN);
        selectListPo.setName("查询所有");
        hmHttpRequestDefinitionRepository.save(selectListPo);
        HmHttpRequestDefinitionPO selectPagePo = new HmHttpRequestDefinitionPO();
        selectPagePo.setModelId(modelId);
        selectPagePo.setKey(ServiceTypeConstant.SELECT_BY_PAGE);
        selectPagePo.setServiceType(BusinessServiceTypeEnum.BUILT_IN);
        selectPagePo.setName("分页查询");
        hmHttpRequestDefinitionRepository.save(selectPagePo);

        if (supportTree) {
            HmHttpRequestDefinitionPO selectTreePo = new HmHttpRequestDefinitionPO();
            selectTreePo.setModelId(modelId);
            selectTreePo.setKey(ServiceTypeConstant.SELECT_TREE);
            selectTreePo.setServiceType(BusinessServiceTypeEnum.BUILT_IN);
            selectTreePo.setName("树查询");
            hmHttpRequestDefinitionRepository.save(selectTreePo);

        }

    }

    public HmHttpRequestDefinition findById(Long id) {
        HmHttpRequestDefinitionPO po = hmHttpRequestDefinitionRepository.getById(id);
        return BeanUtil.toBean(po, HmHttpRequestDefinition.class);
    }

    public void save(HmHttpRequestDefinition hmHttpRequestDefinition) {
        validField(null, Fields.KEY, hmHttpRequestDefinition.getKey(), hmHttpRequestDefinition.getModelId());
        HmHttpRequestDefinitionPO po = BeanUtil.toBean(hmHttpRequestDefinition, HmHttpRequestDefinitionPO.class);
        hmHttpRequestDefinitionRepository.save(po);
    }

    public void update(HmHttpRequestDefinition hmHttpRequestDefinition) {
        HmHttpRequestDefinitionPO po = BeanUtil.toBean(hmHttpRequestDefinition, HmHttpRequestDefinitionPO.class);
        hmHttpRequestDefinitionRepository.updateById(po);
    }

    public void remove(List<Long> ids) {
        hmHttpRequestDefinitionRepository.removeByIds(ids);
    }

    public List<HmHttpRequestDefinition> getList(Long modelId) {
        List<HmHttpRequestDefinitionPO> pos = hmHttpRequestDefinitionRepository.findByModelId(modelId);
        return Convert.toList(HmHttpRequestDefinition.class, pos);
    }

    public PageBase<HmHttpRequestDefinition> getList(Long modelId, int pageNo, int pageSize) {
        Page<HmHttpRequestDefinitionPO> page = new Page<>(pageNo, pageSize);
        QueryWrapper<HmHttpRequestDefinitionPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(Fields.MODEL_ID, modelId);
        queryWrapper.orderByDesc(DbConstants.CREATE_DATE);
        IPage<HmHttpRequestDefinitionPO> resultPage =
                hmHttpRequestDefinitionRepository.selectPage(queryWrapper, page);
        return PageBase.from(resultPage, HmHttpRequestDefinition.class);
    }

    public void validField(Long id, String field, String value, Long modelId) {


        if (Fields.KEY.equals(field)) {
            HmHttpRequestDefinitionPO po = hmHttpRequestDefinitionRepository.findByKeyInModel(value, modelId);
            if (po == null) {
                return;
            }
            if (id == null) {
                throw new ErrorKeyException(I18nKeyEnum.HTTP_REQUEST_DEFINITION_KEY_EXISTS);
            }
            if (po.getId().longValue() != id.longValue()) {
                throw new ErrorKeyException(I18nKeyEnum.HTTP_REQUEST_DEFINITION_KEY_EXISTS);
            }
        }
    }

    public ResponseEntity<?> execute(String modelKey, Long pageId, String key, Map<String, Object> values, Integer pageNo, Integer pageSize, List<String> orderRules) throws SQLException, IOException {
        HmModel hmModel = null;
        if (StringUtils.hasLength(modelKey)) {
            HmModelPO hmModelPO = hmModelRepository.findByCode(modelKey);
            hmModel = BeanUtil.toBean(hmModelPO, HmModel.class);
        } else {
            hmModel = hmModelService.findByPageId(pageId);
        }

        HmHttpRequestDefinitionPO definitionPO = hmHttpRequestDefinitionRepository.findByKeyInModel(key, hmModel.getId());
        if (hmModel.getType() == ModelTypeEnum.ENTITY) {
            return getEntityModelData(hmModel, definitionPO, values, pageNo, pageSize, orderRules);
        }

        if (hmModel.getType() == ModelTypeEnum.VIEW) {
            return getViewModelData(hmModel, key, values, pageNo, pageSize, orderRules);
        }


        //以下是第三方服务
        HmHttpServicePO servicePO = hmHttpServiceRepository.getById(hmModel.getHttpServiceId());
        HttpServiceSettingPO settingPO = httpServiceSettingRepository.findByServiceIdAndEnv(hmModel.getHttpServiceId(), Enum.valueOf(EnvEnum.class, env));
        Request.Builder builder = new Request.Builder();
        String url = httpServiceHelper.getReqUrl(servicePO, settingPO, definitionPO.getPath());
        Map<String, Object> valueCopyMap = values == null ? new HashMap<>(0) : new HashMap<>(values);
        if (servicePO.getAuthType() == AuthTypeEnum.TOKEN) {
            String token = httpServiceHelper.getToken(servicePO.getKey());
            valueCopyMap.put("token", token);
        }
        StringSubstitutor sub = new StringSubstitutor(valueCopyMap);
        //处理header
        String headerJson = definitionPO.getHeader();
        if (StringUtils.hasLength(headerJson)) {
            String replaceHeaderJson = sub.replace(headerJson);
            List<Map<String, String>> headerList = HmHttpRequestDefinition.json2List(replaceHeaderJson);
            for (Map<String, String> paramMap : headerList) {
                String name = paramMap.get("name");
                String value = paramMap.get("value");
                builder.addHeader(name, value);
            }
        }
        //处理Query
        String queryJson = definitionPO.getQuery();
        if (StringUtils.hasLength(queryJson)) {
            String replaceQueryJson = sub.replace(queryJson);
            String queryStr = getQueryStr(replaceQueryJson);
            if (url.contains("?")) {
                url += "&" + queryStr;
            } else {
                url += "?" + queryStr;
            }
        }
        builder.url(url);
        //处理body
        if (definitionPO.getMethod() == HttpMethodEnum.GET) {
            //GET请求 忽略body
            builder.get();
        } else {
            String replaceBodyJson = null;
            if (StringUtils.hasLength(definitionPO.getBody())) {
                replaceBodyJson = sub.replace(definitionPO.getBody());
            }
            RequestBody requestBody = getRequestBody(replaceBodyJson, definitionPO);
            if (definitionPO.getMethod() == HttpMethodEnum.POST) {
                builder.post(requestBody);
            } else if (definitionPO.getMethod() == HttpMethodEnum.PUT) {
                builder.put(requestBody);
            } else {
                builder.delete(requestBody);
            }
        }
        try {

            String responseStr = OkHttpUtils.execNewCall(okHttpClient, builder.build());
            Tuple rspExpress = getResponseExpress(definitionPO.getResponse());
            Configuration configuration = Configuration.builder().jsonProvider(new JacksonJsonProvider()).build();
            DocumentContext context = JsonPath.using(configuration).parse(responseStr);
            String safeCodeStr = rspExpress.get(0).toString().trim();
            Integer code = getTernaryOperatorCode(safeCodeStr, context);

            String safeDataStr = rspExpress.get(1).toString().replaceAll(" ", "");
            Object data = context.read(safeDataStr);
            String safeMessageStr = rspExpress.get(2).toString().replaceAll(" ", "");
            String message = context.read(safeMessageStr);
            return ResponseEntity.create(code, message, data);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ErrorKeyException(I18nKeyEnum.SYSTEM_ERROR);
        }
    }

    public String getReqUrl(Long httpServiceId, String path) {
        HmHttpServicePO servicePO = hmHttpServiceRepository.getById(httpServiceId);
        HttpServiceSettingPO settingPO = httpServiceSettingRepository.findByServiceIdAndEnv(httpServiceId, Enum.valueOf(EnvEnum.class, env));
        return httpServiceHelper.getReqUrl(servicePO, settingPO, path);
    }

    private Integer getTernaryOperatorCode(String safeCodeStr, DocumentContext context) {
        Matcher matcher = PATTERN.matcher(safeCodeStr);
        if (matcher.find()) {
            String leftValue = matcher.group(2);
            //数字类型
            if (leftValue.chars().allMatch(Character::isDigit)) {
                Integer leftValueInt = context.read(matcher.group(1), new TypeRef<Integer>() {
                });
                if (leftValueInt == Integer.parseInt(leftValue)) {
                    return Integer.parseInt(matcher.group(3));
                } else {
                    return Integer.parseInt(matcher.group(4));
                }
            } else {
                String leftValueStr = context.read(matcher.group(1));
                if (leftValueStr.equals(leftValue)) {
                    return Integer.parseInt(matcher.group(3));
                } else {
                    return Integer.parseInt(matcher.group(4));
                }
            }
        }

        return context.read(safeCodeStr, new TypeRef<Integer>() {
        });
    }

    private ResponseEntity<?> getViewModelData(HmModel hmModel, String key, Map<String, Object> values, Integer pageNo, Integer pageSize, List<String> orderRules) throws SQLException {
        if (ServiceTypeConstant.SELECT_LIST.equals(key)) {
            List<Map<String, Object>> dataList = dialectDecision.getService(hmModel.getDatasourceId()).list(BeanUtil.toBean(hmModel, HmModelPO.class), values, orderRules);
            return ResponseEntity.ok(dataList);
        }
        if (ServiceTypeConstant.SELECT_BY_PAGE.equals(key)) {
            PageBase<Map<String, Object>> resultPage = dialectDecision.getService(hmModel.getDatasourceId()).pageList(BeanUtil.toBean(hmModel, HmModelPO.class), values, orderRules, pageNo, pageSize);
            return ResponseEntity.ok(resultPage);
        }
        throw new IllegalArgumentException(key);
    }

    private ResponseEntity<?> getEntityModelData(HmModel hmModel, HmHttpRequestDefinitionPO definitionPO, Map<String, Object> values, Integer pageNo, Integer pageSize, List<String> orderRules) throws IOException {
        if(definitionPO.getServiceType() == BusinessServiceTypeEnum.SCRIPT){
            hmScriptService.execute(definitionPO.getScriptId(),values);
            return ResponseEntity.ok();
        }
        //以下为内置的业务服务
        String key = definitionPO.getKey();
        switch (key){
            case ServiceTypeConstant.INSERT:
                hmModelService.addData(hmModel.getCode(),hmModel.getId(), values);
                return ResponseEntity.ok();
            case ServiceTypeConstant.UPDATE:
                hmModelService.editData(hmModel.getCode(),hmModel.getId(), values);
                return ResponseEntity.ok();
            case ServiceTypeConstant.DELETE:
                // 删除
                Long dataId = Long.parseLong(values.get(DbConstants.ID).toString());
                hmModelService.deleteData(hmModel.getCode(), dataId);
                return ResponseEntity.ok();
            case ServiceTypeConstant.SELECT_LIST:
                // 模型字段关联业务模型和关联数据字典的特殊处理
                if (values.containsKey(PARAM_NAME_RELATION_TYPE)) {
                    return relationColumnProcess(values);
                }
                List<Map<String, Object>> dataList = hmModelRepository.dynamicList(hmModel.getCode(), values, orderRules);
                return ResponseEntity.ok(dataList);
            case ServiceTypeConstant.SELECT_ONE:
                // 列表查询
                Long id = Long.parseLong(values.get(DbConstants.ID).toString());
                Map<String, Object> dataMap = hmModelService.dynamicGet(hmModel.getCode(),hmModel.getId(), id);
                return ResponseEntity.ok(dataMap);
            case ServiceTypeConstant.SELECT_BY_PAGE:
                // 1.表单中的关联选择字段的结果查询
                // 动态查询数据字段的关联业务模型或数据字典信息，一般用于弹出框组件选择关联内容
                if (values.containsKey(PARAM_NAME_RELATION_TYPE)) {
                    return relationColumnProcessByPage(values,pageNo,pageSize);
                }
                // 2.列表分页查询
                return executeDynamicQueryByPage(hmModel, values, pageNo, pageSize, orderRules);
            case ServiceTypeConstant.SELECT_TREE:
                // 树结构实体模型的查询 - 递归返回整个树结构
                TreeNode treeNode = findEntityModelTree(hmModel.getCode(),values);
                return ResponseEntity.ok(treeNode);

        }
        throw new IllegalArgumentException(key);
    }

    private TreeNode findEntityModelTree(String modelCode, Map<String, Object> filterParameters){
        // 1.根据过滤查询出模型上的所有的数据，注意过滤条件
        List<Map<String, Object>> allNodesInTree = hmModelRepository.dynamicList(modelCode, filterParameters, null);

        if(CollectionUtil.isNotEmpty(allNodesInTree)){
            // 2.过滤根节点（parent_id == null or parent_id=0）
            Map<String, Object> parentNode = null;
            Iterator<Map<String, Object>> it = allNodesInTree.iterator();
            if(it.hasNext() && parentNode == null){
                Map<String, Object> curTreeNode = it.next();
                if(!curTreeNode.containsKey(Fields.PARENT_ID) || curTreeNode.get(Fields.PARENT_ID)==null){
                    // Long parentId = curTreeNode.get(Fields.PARENT_ID)；
                    parentNode = curTreeNode;
                    it.remove();
                }
            }
            // 3.组装树结构
            if(parentNode != null){
                // 递归租住数据结构
                TreeNode parentTreeNode = new TreeNode();
                String uniqueKey = parentNode.get(DbConstants.ID).toString();
                Long id = Long.valueOf(uniqueKey);
                parentTreeNode.setUniqueKey(uniqueKey);
                parentTreeNode.setData(parentNode);
                parentTreeNode.setChildren(TreeNode.getChildren(allNodesInTree,id));
            }
        }
        return null;
    }


    private ResponseEntity<?> executeDynamicQueryByPage(HmModel hmModel, Map<String, Object> values,
            Integer pageNo, Integer pageSize, List<String> orderRules) {
        /**
         处理流程：
             Step1：查询模型中的“关联选择”字段详情信息，并构建Map<code,HmEntityModelPO>；
             Step2：动态构建sql，规则如下；
             1）关联选择字段，使用 similar to 模糊匹配，格式如：AND {device_info} similar to '%([2])%';
             2）对于非关联选择字段，暂时是用精确匹配；
             Step3：执行分页查询
             Step4：关联选择字段值替换，筛选出查询结果中所有关联字段的ID集合，并更具关联字段查询对应的
             注意：对于字典类型，后期需要返回多语言配置；
         后期优化
             1.字典信息加缓存，使用appUniqueKey_id作为key；
             2.关联业务模型的解析结果，加缓存，最好能关联到具体的使用哦位置（modeCode.entityModeCode）：
             3.关联数据字典做多语言；
            4.非关联选择字段的模糊匹配
         */
        IPage<Map<String, Object>> resultPage = null;
        // Step1：查询模型中的具备关联选择特性字段实体信息，并构建Map<code,HmEntityModelPO>；
        // 具备关联选择特性：1）关联业务模型或关联特殊模型（如：字典、组织架构）；
        // 2）保存字段值时使用诸如“[1][2]”的字符串，查询使用诸如“[2]|[4]”的字符串；
        Map<String, HmEntityModelPO> relationSelectEntityModelMap = hmEntityModelRepository.
                findEntityModelsMapByModelIdAndType(hmModel.getId(),
                        EntityModelTypeEnum.RELATION_SELECT.toString(),
                        EntityModelTypeEnum.DEPARTMENT.toString(),
                        EntityModelTypeEnum.STAFF.toString());
        if(CollectionUtil.isNotEmpty(relationSelectEntityModelMap)){
            String modelCode = hmModel.getCode();

            // Step2：动态查询模型数据记录
            resultPage = dynamicQueryModelRecords(relationSelectEntityModelMap, modelCode, values,
                    pageNo, pageSize,
                    orderRules);

            // Step3：计算并替换关联选择字段的实际显示内容
            List<Map<String, Object>> records = resultPage.getRecords();
            if(CollectionUtil.isNotEmpty(records)){
                // 遍历查询结果，对于关联选择字段，逐个解析ids、显示规则、查询关联信息并替换为显示值
                records.forEach(item->{
                    // 遍历具备关联选择特性的模型实体字段，并将查询结果翻译成真实显示值
                    for(Entry<String,HmEntityModelPO> entry : relationSelectEntityModelMap.entrySet()){
                        String columnCode = entry.getKey();
                        HmEntityModelPO entityModel = entry.getValue();
                        // 注意：排除脏数据（如没有设置关联字段存储值的数据）
                        if(item.containsKey(columnCode) && item.get(columnCode) != null){
                            // 3.1 解析关联选择字段的数据id，即：将[1][2]中解析为1,2
                            String storageValue = item.get(columnCode).toString();
                            List<String> relationDataIds = parseIds(storageValue);
                            // 依然需要排除脏数据
                            if(CollectionUtil.isNotEmpty(relationDataIds)){
                                // 3.2 计算实际显示内容
                                // 如：关联业务模型的[1][2]应为为“[QJ001]注塑机,[QJ001]堆垛机”
                                // 如：关联数据字典的[1][2]应为为“一般,严重”
                                String realDisplayValue = null;
                                switch (entityModel.getType()){
                                    case DEPARTMENT:
                                        realDisplayValue = getDisplayValue4Department(relationDataIds);
                                        break;
                                    case STAFF:
                                        realDisplayValue = getDisplayValue4User(relationDataIds);
                                        break;
                                    case RELATION_SELECT:
                                        switch (entityModel.getRelationType()){
                                            case BUSINESS:
                                                realDisplayValue = getRealDisplayValue4Business(entityModel.getConfig(),
                                                        relationDataIds);
                                                break;
                                            default:
                                                realDisplayValue = getDisplayValue4Dictionary(relationDataIds);
                                                break;
                                        }
                                        break;
                                    default:
                                        break;
                                }
                                // 5.3 替换关联选择字段内容为实际显示的内容
                                item.put(columnCode,realDisplayValue);
                            }
                        }
                    }
                });
            }
        }else{
            // 不包含关联选择字段，则直接查询
            resultPage = hmModelRepository
                    .dynamicPageList(hmModel.getCode(), values, orderRules,
                            new Page(pageNo, pageSize));
        }
        PageBase<Map<String, Object>> pageBase = new PageBase<>();
        pageBase.setPageNo(pageNo);
        pageBase.setPageSize(pageSize);
        resultPage.getRecords().forEach(item->{
            item.put("create_date",((Timestamp)item.get("create_date")).toLocalDateTime());
            item.put("modify_date",((Timestamp)item.get("modify_date")).toLocalDateTime());
        });
        pageBase.setData(resultPage.getRecords());
        pageBase.setTotalPage(resultPage.getPages());
        pageBase.setTotalCount(resultPage.getTotal());
        return ResponseEntity.ok(pageBase);
    }

    /**
     * 动态查询模型的数据记录，包含关联选择特性字段的数据模型
     * @param relationSelectEntityModelMap
     * @param modelCode
     * @param values
     * @param pageNo
     * @param pageSize
     * @param orderRules
     * @return
     */
    private IPage<Map<String, Object>> dynamicQueryModelRecords(
            Map<String, HmEntityModelPO> relationSelectEntityModelMap, String modelCode,
            Map<String, Object> values, Integer pageNo, Integer pageSize, List<String> orderRules) {
        // 构建过滤条件和排序规则
        String whereSqlSegment = buildWhereSqlSegment(values,
                relationSelectEntityModelMap);
        String orderBySqlSegment = null;
        if(CollectionUtils.isEmpty(orderRules)){
            orderBySqlSegment = "create_date desc,id desc";
        }else{
            StringJoiner joiner = new StringJoiner(",");
            orderRules.forEach(joiner::add);
            orderBySqlSegment = joiner.toString();
        }

        // 动态分页查询模型的数据记录
        IPage<Map<String, Object>> resultPage = hmModelRepository
                .dynamicPageList(modelCode, whereSqlSegment, orderBySqlSegment,
                        new Page(pageNo, pageSize));
        return resultPage;
    }

    private String buildWhereSqlSegment(Map<String, Object> values,
            Map<String, HmEntityModelPO> relationSelectEntityModelMap) {
        String whereSqlSegment = "1=1";
        if(CollectionUtil.isNotEmpty(values)){
            // AND {device_info} similar to '%([2])%'
            StringBuilder sqlBuilder =new StringBuilder(whereSqlSegment);
            for(Entry<String,Object> entry: values.entrySet()){
                if(!postgresDialectService.valid(entry.getValue())){
                    continue;
                }
                if(relationSelectEntityModelMap.containsKey(entry.getKey())){
                    // 关联选择字段，使用 SIMILAR TO 模糊匹配，格式如：AND {device_info} SIMILAR TO '%([2])%';
                    sqlBuilder.append(" AND ");
                    sqlBuilder.append(entry.getKey());
                    sqlBuilder.append("SIMILAR TO '%(");
                    sqlBuilder.append(entry.getValue());
                    sqlBuilder.append(")%'");
                }else{
                    // 对于非关联选择字段，暂时是用精确匹配；
                    // TODO - tylr 2021/6/8 6:32 后期可以改成字符串的模糊匹配
                    sqlBuilder.append(" AND ");
                    sqlBuilder.append(entry.getKey());
                    sqlBuilder.append("=");
                    sqlBuilder.append(postgresDialectService.getValue(entry.getValue()));
                }
            }

        }
        return whereSqlSegment;
    }

    private String getDisplayValue4Dictionary(List<String> relationDataIds) {
        // 5.3 关联数据字典
        // 5.3.1 按照 relationDataIds 查询关联的字典信息
        List<Long> ids = relationDataIds.stream().map(Long::new).collect(
                Collectors.toList());
        List<HmDictionaryPO> dictionaryPOs = hmDictionaryRepository.listByIds(ids);
        if(CollectionUtil.isNotEmpty(dictionaryPOs)){
            // 5.3.2 替换为 optionNames
            List<String> optionNames = dictionaryPOs.stream()
                    .map(HmDictionaryPO::getOptionName)
                    .collect(Collectors.toList());
            return String.join(",",optionNames);
        }
        return null;
    }

    private String getDisplayValue4Department(List<String> relationDataIds) {
        List<Long> ids = relationDataIds.stream().map(Long::new).collect(
                Collectors.toList());
        List<HmOrgPO> orgs = findOrgByIds(ids);
        if(CollectionUtil.isNotEmpty(orgs)){
            // 5.3.2 替换为 names
            List<String> departmentName = orgs.stream()
                    .map(HmOrgPO::getName)
                    .collect(Collectors.toList());
            return String.join(",",departmentName);
        }
        return null;
    }

    private String getDisplayValue4User(List<String> relationDataIds){
        List<Long> ids = relationDataIds.stream().map(Long::new).collect(
                Collectors.toList());
        List<HmUserPO> data = findUsersByIds(ids);
        if(CollectionUtil.isNotEmpty(data)){
            List<String> dataNames = data.stream()
                    .map(HmUserPO::getName)
                    .collect(Collectors.toList());
            return String.join(",",dataNames);
        }
        return null;
    }


    private String getRealDisplayValue4Business(String entityModelConfig, List<String> relationDataIds) {
        // 5.2 关联业务模型数据字段的特殊处理
        // 5.2.1 从 config 中获取关联的业务模型code和显示规则
        String relationModelCode = JsonPath
                .read(entityModelConfig, "$."+PARAM_NAME_BUSINESS_RELATION_MODEL_CODE);
        String displayRule = JsonPath
                .read(entityModelConfig, "$."+PARAM_NAME_BUSINESS_DISPLAY_RULE);
        // 解析显示规则
        FieldDisplayRuleBO fieldDisplayRule = FieldDisplayRuleParser.getFieldDisplayRuleResult(displayRule);

        // 5.2.2 根据 relationModelCode 和 relationDataIds 查询该字段关联业务模型的数据
        List<Map<String, Object>> relationData = hmModelRepository
                .dynamicGetByIds(relationModelCode,
                        fieldDisplayRule.getRelationColumns(),
                        relationDataIds);
        if(CollectionUtil.isNotEmpty(relationData)){
            List<String> displayValues = new ArrayList<>();
            relationData.forEach(it->{
                // 5.2.3 按照设置的显示规则格式化实际显示值
                String[] relationColumnValues = new String[fieldDisplayRule.getRelationColumns().size()];
                for(int i=0;i<fieldDisplayRule.getRelationColumns().size();i++){
                    Object columnValue = it.get(fieldDisplayRule.getRelationColumns().get(i));
                    if(columnValue == null){
                        relationColumnValues[i] = ERROR_VALUE_MISSING;
                    }else{
                        relationColumnValues[i] = columnValue.toString();
                    }
                }
                String curDisplayValue = String.format(fieldDisplayRule
                                .getFieldDisplayFormat(),relationColumnValues);
                displayValues.add(curDisplayValue);
            });
            // 5.2.3 替换为实际显示值
            return String.join(",",displayValues);
        }
        return null;
    }


    /****************************** id解析 ******************************/
    private static final Pattern RELATION_ID_PARSE_PATTERN = Pattern.compile("\\[(\\d+)\\]");

    private List<String> parseIds(String srcIds){
        List<String> ids = new ArrayList<>();
        Matcher matcher = RELATION_ID_PARSE_PATTERN.matcher(srcIds);
        while (matcher.find()) {
            ids.add(matcher.group(1));
        }
        return ids;
    }


    private ResponseEntity<?> relationColumnProcessByPage(Map<String, Object> values, Integer pageNo,Integer pageSize) {
        String relationType = values.get(PARAM_NAME_RELATION_TYPE).toString();
        RelationTypeEnum relationTypeEnum = RelationTypeEnum.from(relationType);
        if (relationTypeEnum == null) {
            throw new ErrorKeyException(I18nKeyEnum.BAD_RELATION_TYPE);
        }
        // 数据字段关联数据字典
        if(relationTypeEnum == RelationTypeEnum.DICTIONARY){
            // 模型字段关联数据字典，请求参数（values）中的 relationType、value，pageNo，pageSize
            // 应用场景：app中创建表单，对“关联数据字典”的字段进行设置
            if(!values.containsKey(PARAM_NAME_DICTIONARY_TYPE)){
                throw new ErrorKeyException(I18nKeyEnum.RELATION_DICTIONARY_TYPE_MISSING);
            }
            String dictionaryType = values.get(PARAM_NAME_DICTIONARY_TYPE).toString();
            QueryWrapper<HmDictionaryPO> queryWrapper = new QueryWrapper<>();
            queryWrapper.like(StringUtil.isNotBlank(dictionaryType),"type",dictionaryType);
            queryWrapper.orderByAsc("create_date");
            IPage<HmDictionaryPO> dictionaryPage = hmDictionaryRepository
                    .selectPage(queryWrapper, new Page<>(pageNo, pageSize));
            List<FieldRelationInfo> fieldRelationInfos = new ArrayList<>();
            dictionaryPage.getRecords().forEach(item->{
                fieldRelationInfos.add(new FieldRelationInfo(item.getId(),item.getOptionName()));
            });
            PageBase<FieldRelationInfo> fieldRelationInfoPage = buildFieldRelationInfoPage(dictionaryPage,
                    fieldRelationInfos);
            return ResponseEntity.ok(fieldRelationInfoPage);
        }else{
            // 数据字段关联业务模型，应用场景：app中创建表单，对“关联业务模型”的字段进行设置
            // 下拉列表或弹出框组件获取数据的请求参数：modelCode、value，pageNo，pageSize
            String subModelCode =  ConvertUtil.getValueFromMap(values,
                    PARAM_NAME_BUSINESS_RELATION_MODEL_CODE,String.class);
            String displayRule =  ConvertUtil.getValueFromMap(values,PARAM_NAME_BUSINESS_DISPLAY_RULE,String.class);
            if(StringUtil.isBlank(subModelCode)
                    || StringUtil.isBlank(displayRule)){
                throw new ErrorKeyException(I18nKeyEnum.RELATION_BUSINESS_INFO_MISSING);
            }
            // 解析关联业务模型的字段显示规则
            FieldDisplayRuleBO fieldDisplayRule = FieldDisplayRuleParser.getFieldDisplayRuleResult(displayRule);
            if(CollectionUtil.isEmpty(fieldDisplayRule.getRelationColumns())){
                throw new ErrorKeyException(I18nKeyEnum.RELATION_BUSINESS_DISPLAY_RULE_ERROR);
            }

            IPage<Map<String, Object>> iPage = hmModelRepository.dynamicPageList(subModelCode, null,
                    Lists.newArrayList("create_date desc"), new Page(pageNo,pageSize));
            List<FieldRelationInfo> fieldRelationInfos = new ArrayList<>();
            iPage.getRecords().forEach(item->{
                Long id = ConvertUtil.getValueFromMap(item,"id",Long.class);
                String displayValue = FieldDisplayRuleParser.getRelationColumnDisplayValue(item,fieldDisplayRule);
                fieldRelationInfos.add(new FieldRelationInfo(id,displayValue));
            });
            PageBase<FieldRelationInfo> fieldRelationInfoPage = buildFieldRelationInfoPage(iPage,
                    fieldRelationInfos);
            return ResponseEntity.ok(fieldRelationInfoPage);
        }
    }



    private PageBase<FieldRelationInfo> buildFieldRelationInfoPage(IPage iPage,
            List<FieldRelationInfo> fieldRelationInfos) {
        PageBase<FieldRelationInfo> fieldRelationInfoPage = new PageBase<>();
        fieldRelationInfoPage.setData(fieldRelationInfos);
        fieldRelationInfoPage.setPageNo((int)iPage.getCurrent());
        fieldRelationInfoPage.setPageSize((int)iPage.getSize());
        fieldRelationInfoPage.setTotalCount(iPage.getTotal());
        fieldRelationInfoPage.setTotalPage(iPage.getPages());
        return fieldRelationInfoPage;
    }

    private ResponseEntity<?> relationColumnProcess(Map<String, Object> values) {
        String relationType = values.get(PARAM_NAME_RELATION_TYPE).toString();
        RelationTypeEnum relationTypeEnum = RelationTypeEnum.from(relationType);
        if (relationTypeEnum == null) {
            throw new ErrorKeyException(I18nKeyEnum.BAD_RELATION_TYPE);
        }
        // 获取公共存在的查询条件：keyword 或 limit
        String keyword = ConvertUtil.getValueFromMap(values,PARAM_NAME_KEYWORD,String.class);
        Integer limit = ConvertUtil.getValueFromMap(values,PARAM_NAME_LIMIT,Integer.class);

        // 数据字段关联数据字典
        if(relationTypeEnum == RelationTypeEnum.DICTIONARY){
            // 模型字段关联数据字典：根据请求参数（values）中的 value、keyword(选填)、limit(选填)
            // 应用场景：app中创建表单，对“关联数据字典”的字段进行设置
            if(!values.containsKey(PARAM_NAME_DICTIONARY_TYPE)){
                throw new ErrorKeyException(I18nKeyEnum.RELATION_DICTIONARY_TYPE_MISSING);
            }
            String dictionaryType = values.get(PARAM_NAME_DICTIONARY_TYPE).toString();
            List<HmDictionaryPO> dictionaryDigests = hmDictionaryRepository.digestList(dictionaryType,keyword,limit);
            List<FieldRelationInfo> results = new ArrayList<>();
            if(CollectionUtil.isNotEmpty(dictionaryDigests)){
                dictionaryDigests.forEach(item->{
                    results.add(new FieldRelationInfo(item.getId(),item.getOptionName()));
                });
            }
            return ResponseEntity.ok(results);
        }else{
            // 数据字段关联业务模型，应用场景：app中创建表单，对“关联业务模型”的字段进行设置
            // 下拉列表或弹出框组件获取数据的请求参数：modelCode、value、keyword(选填)、limit(选填)（首次截取）
            String subModelCode =  ConvertUtil.getValueFromMap(values,
                    PARAM_NAME_BUSINESS_RELATION_MODEL_CODE,String.class);
            String displayRule =  ConvertUtil.getValueFromMap(values,PARAM_NAME_BUSINESS_DISPLAY_RULE,String.class);
            if(StringUtil.isBlank(subModelCode)
                    || StringUtil.isBlank(displayRule)){
                throw new ErrorKeyException(I18nKeyEnum.RELATION_BUSINESS_INFO_MISSING);
            }
            // 解析关联业务模型的字段显示规则
            FieldDisplayRuleBO fieldDisplayRule = FieldDisplayRuleParser.getFieldDisplayRuleResult(displayRule);
            if(CollectionUtil.isEmpty(fieldDisplayRule.getRelationColumns())){
                throw new ErrorKeyException(I18nKeyEnum.RELATION_BUSINESS_DISPLAY_RULE_ERROR);
            }

            // 模糊匹配sql片段
            StringBuilder whereSqlSegment = new StringBuilder();
            if(StringUtil.isNotBlank(keyword)){
                // 将显示规则中抽取的关联字段进行模糊匹配的sql片段
                List<String> likeMatcherSqlSegments = new ArrayList<>();
                fieldDisplayRule.getRelationColumns().forEach(item->{
                    likeMatcherSqlSegments.add(item+"::text like '%"+keyword+"%'");
                });
                whereSqlSegment.append(String.format("\nwhere %s ", String.join(" OR ",likeMatcherSqlSegments)));
            }
            // 按照时间倒序排序
            String orderBySql = "\nORDER BY create_date desc";
            // 添加 limit
            String limitSql = "";
            if(limit != null && limit>0){
                limitSql = "\nLIMIT "+limit;
            }
            List<Map<String, Object>> dataList = hmModelRepository
                    .dynamicList(subModelCode, whereSqlSegment.toString(),orderBySql,limitSql);
            if(CollectionUtil.isNotEmpty(dataList)){
                List<FieldRelationInfo> results = new ArrayList<>();
                dataList.forEach(item->{
                    String displayValue = FieldDisplayRuleParser.getRelationColumnDisplayValue(item,fieldDisplayRule);
                    if(StringUtil.isNotBlank(displayValue)){
                        Long id = ConvertUtil.getValueFromMap(item,"id",Long.class);
                        FieldRelationInfo fieldRelationInfo = new FieldRelationInfo(id,displayValue);
                        results.add(fieldRelationInfo);
                    }
                });
                return ResponseEntity.ok(results);
            }
            return ResponseEntity.ok(dataList);
        }
    }

    private Tuple getResponseExpress(String json) {
        List<Map<String, String>> dataList = HmHttpRequestDefinition.json2List(json);
        String code = null;
        String data = null;
        String message = null;
        for (Map<String, String> dataMap : dataList) {
            String name = dataMap.get("name");
            String value = dataMap.get("value");
            if ("code".equals(name)) {
                code = value;
            }
            if ("data".equals(name)) {
                data = value;
            }
            if ("message".equals(name)) {
                message = value;
            }
        }
        return new Tuple(code, data, message);
    }


    private RequestBody getRequestBody(String replaceBodyJson, HmHttpRequestDefinitionPO definitionPO) {
        if (definitionPO.getBodyType() == BodyTypeEnum.NONE) {
            return RequestBody.create(null, "");
        }
        if (definitionPO.getBodyType() == BodyTypeEnum.FORM_DATA) {
            FormBody.Builder builder = new FormBody.Builder();
            List<Map<String, String>> dataList = HmHttpRequestDefinition.json2List(replaceBodyJson);
            for (Map<String, String> data : dataList) {
                String name = data.get("name");
                String value = data.get("value");
                builder.add(name, value);
            }
            return builder.build();
        }
        if (definitionPO.getBodyType() == BodyTypeEnum.X_WWW_FORM_URLENCODED) {
            MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
            return RequestBody.create(mediaType, replaceBodyJson);

        }
        if (definitionPO.getBodyType() == BodyTypeEnum.RAW) {
            MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
            return RequestBody.create(mediaType, replaceBodyJson);
        }
        throw new IllegalArgumentException();
    }



    private String getQueryStr(String json) {
        List<Map<String, String>> params = HmHttpRequestDefinition.json2List(json);
        return httpServiceHelper.toQueryStr(params);
    }



}